package com.chungkui.check.handler.impl;

import com.chungkui.check.configparser.bean.CheckConfig;
import com.chungkui.check.core.bean.CheckResult;
import com.chungkui.check.handler.CheckHandler;
import com.chungkui.ratelimiter.RateLimiterCache;
import com.chungkui.ratelimiter.StandAloneRateLimiter;
import com.chungkui.ratelimiter.RedisRateLimiter;
import com.chungkui.ratelimiter.impl.StandAloneRateLimiterImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * @Author: jason
 * @Date: 2019/12/30 16:51
 * @Version 1.0
 */

public class RateLimterCheckHandler implements CheckHandler {
    private final Logger log = LoggerFactory.getLogger(RateLimterCheckHandler.class);
    @Autowired(required = false)
    RateLimiterCache rateLimiterCache;
    @Autowired(required = false)
    RedisRateLimiter redisRateLimiter;

    @Override
    public CheckResult check(CheckConfig checkConfig, Map<String, Object> params) {
        List<CheckConfig.RateLimterConfig> rateLimterConfigs = checkConfig.getRateLimterConfigs();
        List<CheckConfig.RedisRateLimterConfig> redisRateLimterConfigs = checkConfig.getRedisRateLimterConfigs();
        if (CollectionUtils.isNotEmpty(redisRateLimterConfigs)) {
            int i = 0;
            for (CheckConfig.RedisRateLimterConfig redisRateLimterConfig : redisRateLimterConfigs) {
                if (redisRateLimterConfig != null) {
                    String fix = redisRateLimterConfig.getFix();
                    int rate = redisRateLimterConfig.getRate();
                    int capacity = redisRateLimterConfig.getCapacity();
                    int perUser = redisRateLimterConfig.getPerUser();
                    if (StringUtils.isEmpty(fix)) {
                        fix = String.valueOf(i);
                    }
                    String key = checkConfig.getKey() + fix;
                    String dynamicParam = redisRateLimterConfig.getDynamicParam();
                    if (StringUtils.isNotEmpty(dynamicParam)) {
                        key = key + "?param=" + MapUtils.getString(params, dynamicParam);
                    }
                    try {
                        boolean result = redisRateLimiter.check(key, rate, capacity, perUser);
                        if (!result) {
                            return CheckResult.fail(redisRateLimterConfig.getCode(), redisRateLimterConfig.getMsg());
                        }
                    } catch (IOException e) {
                        log.error("check error", e);
                    }
                }
            }
        }

        if (CollectionUtils.isNotEmpty(rateLimterConfigs)) {
            int i = 0;
            for (CheckConfig.RateLimterConfig rateLimterConfig : rateLimterConfigs) {
                if (rateLimterConfig != null) {
                    CheckResult checkResult = check(rateLimterConfig, checkConfig.getKey(), params, i);
                    i++;
                    if (!checkResult.ifPass()) {
                        return checkResult;
                    }
                }
            }
        }

        return new CheckResult(true);
    }

    private CheckResult check(CheckConfig.RateLimterConfig rateLimterConfig, String checkConfigKey, Map<String, Object> params, int index) {
        if (rateLimterConfig != null) {
            String dynamicParam = rateLimterConfig.getDynamicParam();
            if (StringUtils.isNotEmpty(dynamicParam)) {
                return dynamicCheck(rateLimterConfig, checkConfigKey, params, index);
            } else {
                return normalCheck(rateLimterConfig, checkConfigKey);
            }

        }
        return new CheckResult(true);
    }

    private CheckResult normalCheck(CheckConfig.RateLimterConfig rateLimterConfig, String key) {
        boolean result;
        StandAloneRateLimiter standAloneRateLimiter = rateLimiterCache.get(key);
        if (standAloneRateLimiter == null) {
            log.debug("流控规则初始化未完成直接放行并进行初始化！");
            rateLimiterCache.set(key, buildRateLimiterService(rateLimterConfig));
            return new CheckResult(true);
        }
        result = standAloneRateLimiter.check();
        if (result) {
            return CheckResult.success();
        } else {
            return CheckResult.fail(rateLimterConfig.getCode(), rateLimterConfig.getMsg());
        }
    }

    private CheckResult dynamicCheck(CheckConfig.RateLimterConfig rateLimterConfig, String checkConfigKey, Map<String, Object> params, int index) {
        String fix = rateLimterConfig.getFix();
        double createRate = rateLimterConfig.getCreateRate();
        if (StringUtils.isEmpty(fix)) {
            fix = String.valueOf(index);
        }
        String dynamicParam = rateLimterConfig.getDynamicParam();
        String key = checkConfigKey + fix + "?param=" + MapUtils.getString(params, dynamicParam);
        boolean result;
        StandAloneRateLimiter standAloneRateLimiter = rateLimiterCache.getDynamic(key);
        StandAloneRateLimiter createStandAloneRateLimiter = null;
        if (createRate != 0) {
            createStandAloneRateLimiter = rateLimiterCache.get(checkConfigKey);
            if (createStandAloneRateLimiter == null) {
                //todo 支持阻塞方式的流控方案
                rateLimiterCache.set(checkConfigKey, new StandAloneRateLimiterImpl(rateLimterConfig.getCreateRate(), rateLimterConfig.getWarmupPeriod(), rateLimterConfig.getSync(), 0L));
            }

        }
        if (standAloneRateLimiter == null) {
            if (createStandAloneRateLimiter != null) {
                boolean createResult = createStandAloneRateLimiter.check();
                if (!createResult) {
                    return CheckResult.fail(rateLimterConfig.getCode(), rateLimterConfig.getMsg());
                }
            }
            log.debug("流控规则初始化未完成直接放行并进行初始化！source:{}", key);
            rateLimiterCache.setDynamic(key, buildRateLimiterService(rateLimterConfig));
            return CheckResult.success();
        }
        result = standAloneRateLimiter.check();
        if (result) {
            return CheckResult.success();
        } else {
            return CheckResult.fail(rateLimterConfig.getCode(), rateLimterConfig.getMsg());
        }

    }

    private StandAloneRateLimiter buildRateLimiterService(CheckConfig.RateLimterConfig rateLimterConfig) {
        return new StandAloneRateLimiterImpl(rateLimterConfig.getPermitsPerSecond(), rateLimterConfig.getWarmupPeriod(), rateLimterConfig.getSync(), rateLimterConfig.getWaitLimit());

    }

    @Override
    public int sort() {
        return 3;
    }
}
